# !/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
===========================
@Time : 2022/8/21 0021 15:17
@Author : 测试开发工程师
@File : 3. 单例设计模式.py
@Software: PyCharm
============================
"""

"""
为什么要使用单例：
    普通的类就像生产机器，可以生产多个实例
    分为 饿汉式单例 和 懒汉式单例
    单例只允许类一次实例化
    
    
单例只允许类一次实例化，同一个对的多个线程同时写入会造成资源竞争问题。比如两个线程同时给一个共享变量 加 1，共享变量最后的结果可能只加了 1

   线程1               对象              线程2
读共享变量1                           读共享变量1
对共享变量 +1                         对共享变量 +1

如果是同一个对象，加入对象级别互斥锁 就能解决问题
对象 ->  读共享变量1  ->  对共享变量 +1  ->  读共享变量2  -> 对共享变量+1
    
饿汉式单例： 
    在类加载时就创建好实例
    类加载 -> 类使用

懒汉式单例：

"""
# 饿汉式单例
class IdMaker:
    # python 的类变量会被多个类，实例共享
    __instance = None
    # id 也是类变量，多个实例或类共享
    __id = -1
    # 类加载，在创建实例化之前。
    # 先使用父类的 __new__(返回实例),再进行初始化__init__
    # new 方法重写父类的 __new__
    # python 在类加载阶段，通过父类的 __new__ 创建实例，
    # 如果我们重写 __new__,就不会调用父类的 __new__,就会调用我们写的 __new__ 创建实例


    # __new__ 方法 需要返回一个实例，如果不返回，就不会实例化
    def __new__(cls):
        if cls.__instance is None:
            # 父类的 __new__,参数接受一个类名，会返回类的实例
            cls.__instance = super().__new__(cls)
        return cls.__instance


    # 计数器，在获取前，进行 +1
    def get_id(self):
        self.__id += 1
        return self.__id

def test_id_maker():
    # IdMaker 是单例类，只允许有一个实例
    id1 = IdMaker().get_id()
    id2 = IdMaker().get_id()
    id3 = IdMaker().get_id()
    print(id1,id2,id3)


if __name__ == '__main__':
    test_id_maker()



from threading import Lock
# 懒汉式单例
class IdMaker:
    # 申请一个线程锁
    __instance_lock = Lock()
    # python 的类变量会被多个类，实例共享
    __instance = None
    # id 也是类变量，多个实例或类共享
    __id = -1

    # 如果 __new__ 抛出异常，就不允许调用者进行实例化
    def __new__(cls):
        raise ImportError('Instantition not allowed')

    # 类方法不要实例化也能调用，因为我们不允许进行实例化，所以要使用类方法
    @classmethod
    def get_instance(cls):
        # with 会帮我们自动的上锁和释放锁。不用我们操心
        with cls.__instance_lock:
            if cls.__instance is None:
                # 因为我们的 __new__ 代码不允许进行实例化，所以可以借用父类的 __new__进行实例化
                cls.__instance = super().__new__(cls)
            return cls.__instance

    # 计数器，在获取前，进行 +1
    def get_id(self):
        self.__id += 1
        return self.__id

def test_id_maker():
    # IdMaker 是单例类，只允许有一个实例
    id1 = IdMaker.get_instance().get_id()
    id2 = IdMaker.get_instance().get_id()
    id3 = IdMaker.get_instance().get_id()
    print(id1,id2,id3)


if __name__ == '__main__':
    test_id_maker()















